<!DOCTYPE html>
<!--
Copyright (c) 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/utils.html">
<link rel="import" href="/tracing/model/helpers/chrome_process_helper.html">

<script>
'use strict';

/**
 * @fileoverview Utilities for accessing trace data about the Chrome browser.
 */
tr.exportTo('tr.model.helpers', function() {
  function ChromeBrowserHelper(modelHelper, process) {
    tr.model.helpers.ChromeProcessHelper.call(this, modelHelper, process);
    this.mainThread_ = process.findAtMostOneThreadNamed('CrBrowserMain');
    if (!process.name) {
      process.name = ChromeBrowserHelper.PROCESS_NAME;
    }
  }

  ChromeBrowserHelper.PROCESS_NAME = 'Browser';

  ChromeBrowserHelper.isBrowserProcess = function(process) {
    return !!process.findAtMostOneThreadNamed('CrBrowserMain');
  };

  ChromeBrowserHelper.prototype = {
    __proto__: tr.model.helpers.ChromeProcessHelper.prototype,

    // TODO(petrcermak): Pass browser name in a metadata event (see
    // crbug.com/605088).
    get browserName() {
      const hasInProcessRendererThread = this.process.findAllThreadsNamed(
          'Chrome_InProcRendererThread').length > 0;
      return hasInProcessRendererThread ? 'webview' : 'chrome';
    },

    get mainThread() {
      return this.mainThread_;
    },

    get rendererHelpers() {
      return this.modelHelper.rendererHelpers;
    },

    getLoadingEventsInRange(rangeOfInterest) {
      return this.getAllAsyncSlicesMatching(function(slice) {
        return slice.title.indexOf('WebContentsImpl Loading') === 0 &&
            rangeOfInterest.intersectsExplicitRangeInclusive(
                slice.start, slice.end);
      });
    },

    getCommitProvisionalLoadEventsInRange(rangeOfInterest) {
      return this.getAllAsyncSlicesMatching(function(slice) {
        return slice.title === 'RenderFrameImpl::didCommitProvisionalLoad' &&
            rangeOfInterest.intersectsExplicitRangeInclusive(
                slice.start, slice.end);
      });
    },

    get hasLatencyEvents() {
      let hasLatency = false;
      for (const thread of this.modelHelper.model.getAllThreads()) {
        for (const event of thread.getDescendantEvents()) {
          if (!event.isTopLevel) continue;
          if (!(event instanceof tr.e.cc.InputLatencyAsyncSlice)) {
            continue;
          }
          hasLatency = true;
        }
      }
      return hasLatency;
    },

    getLatencyEventsInRange(rangeOfInterest) {
      return this.getAllAsyncSlicesMatching(function(slice) {
        return (slice.title.indexOf('InputLatency') === 0) &&
            rangeOfInterest.intersectsExplicitRangeInclusive(
                slice.start, slice.end);
      });
    },

    getAllAsyncSlicesMatching(pred, opt_this) {
      const events = [];
      this.iterAllThreads(function(thread) {
        for (const slice of thread.getDescendantEvents()) {
          if (pred.call(opt_this, slice)) {
            events.push(slice);
          }
        }
      });
      return events;
    },

    iterAllThreads(func, opt_this) {
      for (const thread of Object.values(this.process.threads)) {
        func.call(opt_this, thread);
      }

      for (const rendererHelper of Object.values(this.rendererHelpers)) {
        const rendererProcess = rendererHelper.process;
        for (const thread of Object.values(rendererProcess.threads)) {
          func.call(opt_this, thread);
        }
      }
    }
  };

  return {
    ChromeBrowserHelper,
  };
});
</script>
